/*  Copyright © 2009 Blake Hegerle -*- C++ -*- */

/*  This file is part of metal.
 
    Metal is free software: you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.
 
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see
    <http://www.gnu.org/licenses/>.

*/

#include "meta/module.h"

using namespace metal;
using namespace metal::meta;

using namespace std;

namespace {
  struct f { };
  struct x { };
  struct y { };
  struct g { };
}

typedef bindings<> decls;
typedef set<> defs;

typedef module_definition_binder<
  decls, 
  defs, 
  f,
  ident_expr<x>,
  parameter<short, x>
> good_check;

static_assert(good_check::compatible, "nothing previously declared");
static_assert(!good_check::duplicate, "nothing previously defined");
static_assert(good_check::is_safe, "f = def<short>(arg<short>( x ))( x ) is safe");
static_assert(good_check::is_bound, "f = def<short>(arg<short>( x ))( x ) is bound");

typedef module_definition_binder<
  decls,
  defs,
  f,
  unary_expr<boolean_tag, ident_expr<x> >,
  parameter<short, x>
> unsafe_expr_check;

static_assert(!unsafe_expr_check::is_safe,
	      "f = def<short>(arg<short>( x ))( x ) is safe");

typedef module_definition_binder<
  decls,
  defs, 
  f,
  ident_expr<y>,
  parameter<short, x>
> unbound_check;

static_assert(!unbound_check::is_bound, 
	      "f = def<short>(arg<short>( x ))( y ) is bound");

typedef meta::bind<g, list<float, long>, decls>::type g_decld;

typedef module_definition_binder<
  g_decld,
  defs, 
  f, 
  call_expr<ident_expr<g>, ident_expr<x> >, 
  parameter<long, x>
> call_decld_check;

//typedef call_decld_check::def_check defun_check;

//static_assert(def_check::check::is_safe, "");

static_assert(call_decld_check::compatible, 
	      "f is compatible with g (different idents)");
static_assert(!call_decld_check::duplicate, 
	      "f is not a duplicate of g");
static_assert(call_decld_check::is_safe, 
	      "f = def<float>(arg<long>( x ))( g(x) ) is safe");
static_assert(call_decld_check::is_bound, 
	      "f = def<float>(arg<long>( x ))( g(x) ) is bound");

typedef module_definition_binder<
  decls, 
  defs,
  f, 
  call_expr<g>
> call_unbound_check;

static_assert(!call_unbound_check::is_bound, "f = def<float>()( g() ) is bound");

typedef cond_expr<
  binary_expr<
    comparison_tag, 
    ident_expr<x>, 
    constant_expr<int> 
    >, constant_expr<int>, 
  binary_expr<
    arithmatic_tag, 
    ident_expr<x>, 
    call_expr<
      f, 
      binary_expr<
	arithmatic_tag, 
	ident_expr<x>, 
	constant_expr<int> 
	>
      >
    >
  > factoral_expr;

typedef module_definition_binder<
  decls, 
  defs, 
  f,
  factoral_expr, 
  parameter<int, x>
> fact_binder;

/*
static_assert(fact_binder::compatible, "factorial is compatible");
static_assert(!fact_binder::duplicate, "factorial is not a duplicate");
static_assert(fact_binder::is_safe, "factorial is safe");
static_assert(fact_binder::is_bound, "factorial is bound");
*/
